#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <stdint.h>
#include <string.h>

#define PCM_DEVICE "default"

typedef struct {
    char riff[4];
    uint32_t overall_size;
    char wave[4];
    char fmt_chunk_marker[4];
    uint32_t length_of_fmt;
    uint16_t format_type;
    uint16_t channels;
    uint32_t sample_rate;
    uint32_t byterate;
    uint16_t block_align;
    uint16_t bits_per_sample;
    char data_chunk_header[4];
    uint32_t data_size;
} wav_header_t;

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("用法: %s <wav文件> | [pcm文件 -rate=48000 -channel=2 -bits=16]\n", argv[0]);
        return -1;
    }

    FILE *fp = fopen(argv[1], "rb");
    if (!fp) {
        perror("无法打开文件");
        return -1;
    }

    wav_header_t header;
    memset(&header, 0x00, sizeof(wav_header_t));
    if (strstr(argv[1], ".pcm")) {
        for (int i = 2; i < argc; i++) {
            printf("arv[%d]=%s\n", i, argv[i]);
            if (!strncmp(argv[i], "-rate=", sizeof("-rate=") - 1)) {
                header.sample_rate = atoi(argv[i] + sizeof("-rate=") - 1);
            } else if (!strncmp(argv[i], "-channel=", sizeof("-channel=") - 1)) {
                header.channels = atoi(argv[i] + sizeof("-channel=") - 1);
            } else if (!strncmp(argv[i], "-bits=", sizeof("-bits=") - 1)) {
                header.bits_per_sample = atoi(argv[i] + sizeof("-bits=") - 1);
            }
        }
    } else {
        fread(&header, sizeof(wav_header_t), 1, fp);

        if (memcmp(header.riff, "RIFF", 4) != 0 || memcmp(header.wave, "WAVE", 4) != 0) {
            printf("不是有效的WAV文件\n");
            fclose(fp);
            return -1;
        }
    }

    // -rate=48000 -channel=2 -bits=16
    printf("采样率: %u, 通道数: %u, 位深: %u, 数据大小: %u\n",
        header.sample_rate, header.channels, header.bits_per_sample, header.data_size);

    snd_pcm_t *pcm_handle;
    snd_pcm_hw_params_t *params;
    int pcm;
    snd_pcm_uframes_t frames = 1024;

    // 打开PCM设备
    pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0);
    if (pcm < 0) {
        printf("无法打开PCM设备: %s\n", snd_strerror(pcm));
        fclose(fp);
        return -1;
    }

    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcm_handle, params);
    snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

    // 设置采样格式
    snd_pcm_format_t format;
    if (header.bits_per_sample == 16) {
        format = SND_PCM_FORMAT_S16_LE;
    } else if (header.bits_per_sample == 8) {
        format = SND_PCM_FORMAT_U8;
    } else {
        printf("不支持的位深: %u\n", header.bits_per_sample);
        snd_pcm_close(pcm_handle);
        fclose(fp);
        return -1;
    }
    snd_pcm_hw_params_set_format(pcm_handle, params, format);

    snd_pcm_hw_params_set_channels(pcm_handle, params, header.channels);
    snd_pcm_hw_params_set_rate_near(pcm_handle, params, &header.sample_rate, 0);
    snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &frames, 0);

    pcm = snd_pcm_hw_params(pcm_handle, params);
    if (pcm < 0) {
        printf("无法设置硬件参数: %s\n", snd_strerror(pcm));
        snd_pcm_close(pcm_handle);
        fclose(fp);
        return -1;
    }

    // 播放数据
    int frame_bytes = header.channels * (header.bits_per_sample / 8); // 1 * 16 / 8 = 2
    uint8_t *buffer = malloc(frames * frame_bytes); // 1024 * 2 = 2048
    if (!buffer) {
        printf("内存分配失败\n");
        snd_pcm_close(pcm_handle);
        fclose(fp);
        return -1;
    }

    size_t read;
    while ((read = fread(buffer, 1, frames * frame_bytes, fp)) > 0) {
        int frames_to_write = read / frame_bytes; // 2048 / 2 = 1024
        int written = 0;
        while (written < frames_to_write) {
            int ret = snd_pcm_writei(pcm_handle, buffer + written * frame_bytes, frames_to_write - written); // 1024
            if (ret == -EPIPE) {
                snd_pcm_prepare(pcm_handle);
                continue;
            } else if (ret < 0) {
                printf("写入错误: %s\n", snd_strerror(ret));
                break;
            }
            written += ret;
        }
    }

    snd_pcm_drain(pcm_handle);
    snd_pcm_close(pcm_handle);
    free(buffer);
    fclose(fp);
    printf("播放结束\n");
    return 0;
}
